#include "Terrain.h"

//Include application - we check for resize. 
#include "Application.h"

//Complete forward declarations
#include "SkyDome.h"
#include "StaticModel.h"
#include "TerrainWater.h"

//Uses graphics interface to add static models to batch.
#include "GraphicsInterface.h"

//Used to read PNG files
#include "loadpng.h"

//Outputs a float to the Debug window. 
void Output(float f)
{
	std::stringstream ss; 
	ss << f;
	OutputDebugStringA(ss.str().c_str());
	OutputDebugStringW(L"|");
}

Terrain::Terrain()
{
	mainTerrainSizeMaxX = 0.0f;
	mainTerrainSizeMaxZ = 0.0f;
	mainTerrainSizeMinX = 0.0f;
	mainTerrainSizeMinZ = 0.0f;

	//Init water pointer to 0
	terrainWater = 0;
}

Terrain::~Terrain()
{
	//Delete patches
	for (unsigned i = 0; i < terrainPatches.size(); i++)
		SafeDelete(terrainPatches[i]);
	terrainPatches.clear();

	//Delete static models
	for (unsigned i = 0; i < terrainStaticModels.size(); i++)
		SafeDelete(terrainStaticModels[i]);
	terrainStaticModels.clear();

	//Delete textures
	for (unsigned i = 0; i < textureData.size(); i++)
		SafeDelete(textureData[i]);
	textureData.clear();

	//Delete water
	SafeDelete(terrainWater);
}

void Terrain::Init(ID3D11Device* device, SkyDome* skydome)
{
	if (!isInit)
	{
		//Ensure device is not NULL. 
		assert(device);

		//Store skydome
		this->skyDome = skydome;

		//Precompute the terrain heights.
#ifdef TERRAIN_USE_TYROL
		PreComputeHeights("Terrain/Download/Tyrol/Tyro_1024-png.png", TERRAIN_SCALE_FACTOR);
#endif
#ifdef TERRAIN_USE_SMOOTH_HILLS
		PreComputeHeights("Terrain/Download/Smooth/smooth-hills_1024-png.png", TERRAIN_SCALE_FACTOR);
#endif
#ifdef TERRAIN_USE_HOLE
		PreComputeHeights("Terrain/Download/Hole/the-hole2-png.png", TERRAIN_SCALE_FACTOR);
#endif
#ifdef TERRAIN_USE_ALPINE
		PreComputeHeights("Terrain/Download/Alpine/alpine-terrain-png.png", TERRAIN_SCALE_FACTOR);
#endif
#ifdef TERRAIN_USE_JAGGED
		PreComputeHeights("Terrain/Download/Jagged/jagged-hills-png.png", TERRAIN_SCALE_FACTOR);
#endif

#ifdef TERRAIN_USE_SMOOTH_HILLS_2048 
		PreComputeHeights("Terrain/Download/Smooth/smooth-hills-png.png", TERRAIN_SCALE_FACTOR);
#endif

		//Ensure no holes in the terrain
		CalculateEdgeHeights((UINT)(TERRAIN_SIZE/TARGET_PATCH_SIZE), 
			(UINT)(TERRAIN_SIZE/TARGET_PATCH_SIZE), 
			(UINT)TERRAIN_SIZE, (UINT)TERRAIN_SIZE, 
			(UINT)TARGET_PATCH_SIZE, (UINT)TARGET_PATCH_SIZE);

		/*for (unsigned i = 0; i < TERRAIN_SIZE; i++)
		{
			for (unsigned j = 0; j < TERRAIN_SIZE; j++)
			{
				terrainHeights[j][i] = i;
			}
		}*/

		//Load textures
		LoadTerrainTexData(device);

		//Load terrain patches
		LoadTerrainPatches(device);

		//Once we have loaded the main terrain, calculate the dimentions
		CalculateMainTerrainSize();

		//Load surrounding terrain patches - Those that you can not 
		//walk on. 
		LoadSurroundingTerrainPatches(device);

		//Load the static models - again, can not walk on these. 
		LoadStaticModelTerrainObjects(device);

		//Create the water. 
		CreateWater(device);

		//The Graphics intgerface will NOT do frustum culling. We will 
		//manage it ourselves in the Render() function. 
		subclassManagesFrustumCulling = true;

		//Class inited
		isInit = true;
	}
}

void Terrain::PreComputeHeights(const char* heightMapFull, float heightDivScale)
{
	OutputDebugStringW(L"Pre Computing Heights for: ");
	OutputDebugStringA(heightMapFull);
	OutputDebugStringW(L"\n");

	//Use the third part lodepng library to load a 16bit PNG - Not my fault
	//regarding the spelling...
	//
	//Will be filled with per pixel data.
	std::vector<unsigned char>image;
	//Unsigned ints representing the size of the file (pixels)
	unsigned fileWidth, fileHeight;

	//Decode to extract the per pixel informaton. 
	unsigned error = lodepng::decode(image, fileWidth, fileHeight, heightMapFull, 
		LCT_GREY, 16);

	if (error)
	{
		OutputDebugStringW(L"Terrain Error - Failed to decode png.\n");
		while(1){}
	}

	//We have 16bit uint data (well... 2 8 bit chars per
	//pixel) - convert to a floating point value (32bit)
	//and fillout our heightsOut vector. 
	std::vector<float>heightTemp;
	for(unsigned i=0; i<fileWidth*fileHeight*2; i+=2)
	{
		//2 chars per pixel. We need to merge the two together to get
		//one 16 bit uint value.
		unsigned char first = image[i];
		unsigned char second = image[i+1];

		//Height for this pixel. 
		char16_t height = (char16_t)first << 8 | second;

		//Add to vector.
		heightTemp.push_back((float)height);
	}

	//Ensure that we have one height value per vertex
	if (heightTemp.size() != TERRAIN_SIZE*TERRAIN_SIZE)
	{
		OutputDebugStringW(L"Terrain Error - Mismatch between the TERRAIN_SIZE value and passed in heightmap. Its likely that your TERRAIN_SIZE define doesnt match the heightmap dimntions.\n");
		while(1){}
	}

	//Extract the data in to a 2D vector. 
	for (UINT i = 0; i < TERRAIN_SIZE; i++)
	{
		for (UINT j = 0; j < TERRAIN_SIZE; j++)
		{
			//For this 2D location (i,j)... what is the index
			//in to the 1D vector?
			UINT index = (i*(UINT)TERRAIN_SIZE) + j;
			//Sample the height and scale down. 
			terrainHeights[i][j] = (heightTemp[index]/heightDivScale);
		}
	}
}

void Terrain::CalculateEdgeHeights(UINT numPatchesX, UINT numPatchesZ, 
		UINT patchVertexX, UINT patchVertexZ, 
		UINT patchSizeX, UINT patchSizeZ)
{
	//Horizontal
	//
	//No need to do any work on the absolute edges...
	for (UINT i = 1; i < numPatchesZ; i++)
	{
		for (UINT j = 0; j < patchVertexX; j++)
		{
			//For this texel, sample the above and below value:
			//
			//Say we are at the * texel. We want to sample A and *
			//
			//-------------------A-------------------
			//-------------------*-------------------
			//
			//Then, interpolate the two values. Finally, have A and * store
			//this interpolated value. 
			//
			//A
			float aHeight = terrainHeights[i*patchSizeZ - 1][j];
			//*
			float starHeight = terrainHeights[i*patchSizeZ][j];

			//Interpolate
			float interp = (aHeight+starHeight) / 2.0f;

			//Store this height. 
			terrainHeights[i*patchSizeZ][j] = interp;
			terrainHeights[i*patchSizeZ-1][j] = interp;
		}
	}

	//Verticle
	//
	//No need to do any work on the absolute edges...
	for (UINT i = 1; i < numPatchesX; i++)
	{
		for (UINT j = 0; j < patchVertexZ; j++)
		{
			//Similar to above. We want to sample the value at star
			//and the value to the left of the texel (*).
			//
			//A
			float aHeight = terrainHeights[j][i*patchSizeX - 1];
			//*
			float starHeight = terrainHeights[j][i*patchSizeX];

			//Interpolate
			float interp = (aHeight+starHeight) / 2.0f;

			//Store this height. 
			terrainHeights[j][i*patchSizeX] = interp;
			terrainHeights[j][i*patchSizeX-1] = interp;
		}
	}
}

void Terrain::ExtractHeights(std::vector<float>&out, 
		unsigned patchI, unsigned patchJ, unsigned patchWidth, unsigned patchDepth)
{
	int iStart = (patchI * patchWidth);
	int jStart = (patchJ * patchDepth);

	//Times we loop conciddering the start index. 
	int iLoops = iStart + patchWidth;
	int jLoops = jStart + patchDepth;

	for(int i = iLoops-1; i >= iStart; i--)
	{
		for(int j = jStart; j < jLoops; j++)
		{
			UINT index = (jLoops * (iLoops - i)) + j;
			out.push_back(terrainHeights[i][j]);
		}
	}
}

void Terrain::ExtractMedHeights(std::vector<float>&out, 
		unsigned patchI, unsigned patchJ, unsigned patchWidth, unsigned patchDepth)
{
	int iStart = (patchI * patchDepth);
	int jStart = (patchJ * patchWidth);

	//Times we loop conciddering the start index. 
	int iLoops = iStart + patchDepth;
	int jLoops = jStart + patchWidth;

	//if i == iLast or j == jLast, add ome i or j when
	//sampling the terrainHeights array.
	int iLast = iStart+1;
	int jLast = jLoops-2;
	
	for(int i = iLoops-1; i >= iStart; i--)
	{
		for(int j = jStart; j < jLoops; j++)
		{
			//if i and j are even, we want to take this sample. 
			if ( ((i%2) == 1) && ((j%2) == 0) )
			{
				int iSamp = i;
				int jSamp = j;

				//if i or j == iLast or jLast, increment iSamp. 
				if (iSamp == iLast)
				{
					//OutputDebugStringW(L"I YES\n");
					iSamp--;
				}
				if (jSamp == jLast)
				{
					//OutputDebugStringW(L"J YES\n");
					jSamp++;
				}
								
				//Extract height.
				out.push_back(terrainHeights[iSamp][jSamp]);
			}
		}
	}
}

void Terrain::LoadTerrainTexData(ID3D11Device* device)
{
	//Create textures for the main terrain. 
	TerrainPatchTextureData* texD;
	texD = new TerrainPatchTextureData();

	//Load the (underlay) textures. 
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Download/Tex/detail01.dds", 
		0, 0, &texD->DetailMap0, 0));

	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Grass_5/d.png", 
		0, 0, &texD->DiffuseMap0, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Grass_5/n.png", 
		0, 0, &texD->NormalMap0, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Grass_5/s.png", 
		0, 0, &texD->SpecMap0, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Grass_5/h.png", 
		0, 0, &texD->HeightMap0, 0));

	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_10/d.png", 
		0, 0, &texD->DiffuseMap1, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_10/n.png", 
		0, 0, &texD->NormalMap1, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_10/s.png", 
		0, 0, &texD->SpecMap1, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_10/h.png", 
		0, 0, &texD->HeightMap1, 0));

	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_8/d.png", 
		0, 0, &texD->DiffuseMap2, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_8/n.png", 
		0, 0, &texD->NormalMap2, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_8/s.png", 
		0, 0, &texD->SpecMap2, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Rock_8/h.png", 
		0, 0, &texD->HeightMap2, 0));

	//Layer data.
	//
	//1 == Path.
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/LayerMaps/PathLayer_3.png", 
		0, 0, &texD->LayerMap1, 0));

	//Layer texture data
	//
	//1 == Path
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Path_5/d.png", 
		0, 0, &texD->Layer1Tex, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Path_5/n.png", 
		0, 0, &texD->Layer1Normal, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Path_5/s.png", 
		0, 0, &texD->Layer1Spec, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Textures/Terrain/Path_5/h.png", 
		0, 0, &texD->Layer1Height, 0));

	//Store this new texture struct (index 0) ready to be passed to the
	//patches. 
	textureData.push_back(texD);

	//Create textures for the Volcano
	texD = new TerrainPatchTextureData();
	//Load the textures. 
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Volcano/Tex/Slumbering Volcano.jpg", 
		0, 0, &texD->DiffuseMap0, 0));	
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Volcano/Tex/volc_2_diffuse.jpg", 
		0, 0, &texD->DiffuseMap1, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Volcano/Tex/Pele's Lake.jpg", 
		0, 0, &texD->DiffuseMap2, 0));
	//Simple NM for all layers
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Download/Tex/bump01.dds", 
		0, 0, &texD->NormalMap0, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Download/Tex/bump01.dds", 
		0, 0, &texD->NormalMap1, 0));
	HR(D3DX11CreateShaderResourceViewFromFile(device, L"Terrain/Download/Tex/bump01.dds", 
		0, 0, &texD->NormalMap2, 0));
	//Store this new texture struct (index 0) ready to be passed to the
	//patches. 
	textureData.push_back(texD);
}

void Terrain::LoadTerrainPatches(ID3D11Device* device)
{
	//Load a 2D grid of sub textures (Making up a single 
	//2048*2048 texture) : Smooth Hills. 
	for (int i = 0; i < TERRAIN_SIZE/TARGET_PATCH_SIZE; i++)
	{
		for (int j = 0; j < TERRAIN_SIZE/TARGET_PATCH_SIZE; j++)
		{
			//Ask the terrain class to extract a list of vertex heights
			//for this patch
			std::vector<float>heights;
			std::vector<float>medHeights;

			ExtractHeights(heights, i, j, (UINT)TARGET_PATCH_SIZE, (UINT)TARGET_PATCH_SIZE);
			ExtractMedHeights(medHeights, i, j, (UINT)TARGET_PATCH_SIZE, (UINT)TARGET_PATCH_SIZE);

			//Patch locations. 
			float xLocation = (j*TARGET_PATCH_SIZE) * TERRAIN_CELL_SIZE;
			float zLocation = ((float)TERRAIN_SIZE - (i*TARGET_PATCH_SIZE)) * TERRAIN_CELL_SIZE;
			//Move half a patch size backwards. (Making the back of the terrain == world space
			//location 0)
			zLocation -= TARGET_PATCH_SIZE/2.0f * TERRAIN_CELL_SIZE;
			//And across. 
			xLocation += TARGET_PATCH_SIZE/2.0f * TERRAIN_CELL_SIZE;

			LoadPatchAdvanced(device, heights, medHeights,
				TARGET_PATCH_SIZE, TARGET_PATCH_SIZE, 
				TERRAIN_CELL_SIZE, 
				XMFLOAT3(xLocation, TERRAIN_TRANSLATION_Y, zLocation), 
				0.0f,
				textureData[0], 
				j, i, 
				true);
		}
	}
}

void Terrain::LoadSurroundingTerrainPatches(ID3D11Device* device)
{
	//Volcano
	LoadPatch(device, "Terrain/Volcano/Volc - Copy_3.bmp", false,
		4.0f, 0.5f, 
		XMFLOAT3((TERRAIN_SIZE*(TERRAIN_CELL_SIZE*1.325f))+TERRAIN_SIZE, WATER_HEIGHT+0.5f, +400.0f), //Displace slightly in +Y direction. 
		MathHelper::ToRadians(0.0f), 
		textureData[1], 
		false, 160.0f, 320.0f);
}

void Terrain::LoadStaticModelTerrainObjects(ID3D11Device* device)
{}

void Terrain::CreateWater(ID3D11Device* device)
{
	//Create terrain water
	terrainWater = new TerrainWater();
	terrainWater->Init(device, 16000.0f, 16000.0f, 
		XMFLOAT3(TERRAIN_SIZE/2.0f, WATER_HEIGHT, TERRAIN_SIZE/2.0f));
}

void Terrain::LoadPatch(ID3D11Device* device, 
	const char* heightMapFile,
	bool isPNG,
	float terrainCellSize,
	float heightDivFactor,
	XMFLOAT3 patchLocation, 
	float patchRotYRad, 
	TerrainPatchTextureData* patchTexData, 
	bool isSlopeBased, 
	float height1, float height2)
{
	OutputDebugStringW(L"Loading Terrain Patch: ");
	OutputDebugStringA(heightMapFile);
	OutputDebugStringW(L"\n");

	//Pointer to a new patch and init it. 
	TerrainPatch* patch = new TerrainPatch();
	patch->Init(device, 
		heightMapFile, 
		isPNG,
		terrainCellSize,
		patchTexData, heightDivFactor, 
		isSlopeBased, height1, height2);
	patch->SetWorldPosition(patchLocation);
	patch->SetWorldRotation(XMFLOAT3(0.0f, patchRotYRad, 0.0f));

	//Data required for the vector structure we use. Essentually,
	//the patch and the aabbL in one nice easy place. 
	TerrainPatchData* newP = new TerrainPatchData();
	newP->Patch = patch;
	newP->PatchAABBL = patch->aabbL;
	newP->IsMainTerrainPatch = false; //Surrounding patch

	//Add to our vector
	terrainPatches.push_back(newP);
}

void Terrain::LoadPatchAdvanced(ID3D11Device* device, 
		std::vector<float>&heights, 
		std::vector<float>&medHeights,
		float width, float depth, 
		float terrainCellSize, 
		XMFLOAT3 patchLocation, 
		float patchRotYRad, 
		TerrainPatchTextureData* patchTexData,
		UINT patchIDX, UINT patchIDZ,
		bool isSlopeBased, 
		float height1, float height2)
{
	//For each vertex, how huch to incremement the tex coord for layer
	//tex coords.
	float texCoordStepX = 1.0f / TERRAIN_SIZE;
	float texCoordStepY = 1.0f / TERRAIN_SIZE;

	//Calc the min and max LayerTex coords for this patch.
	float minLayerTexCoordX = (TARGET_PATCH_SIZE * patchIDX) * texCoordStepX;
	float minLayerTexCoordY = (TARGET_PATCH_SIZE * patchIDZ) * texCoordStepY;

	//Pointer to a new patch and init it. 
	TerrainPatch* patch = new TerrainPatch();
	patch->InitAdvanced(device, 
		heights, medHeights,
		width, depth, terrainCellSize, 
		patchTexData,
		texCoordStepX, texCoordStepY, minLayerTexCoordX, minLayerTexCoordY,
		isSlopeBased, height1, height2);
	patch->SetWorldPosition(patchLocation);
	patch->SetWorldRotation(XMFLOAT3(0.0f, patchRotYRad, 0.0f));

	//Data required for the vector structure we use. Essentually,
	//the patch and the aabbL in one nice easy place. 
	TerrainPatchData* newP = new TerrainPatchData();
	newP->Patch = patch;
	newP->PatchAABBL = patch->aabbL;
	newP->PatchIdX = patchIDX;
	newP->PatchIdZ = patchIDZ;
	newP->IsMainTerrainPatch = true;

	//Add to our vector
	terrainPatches.push_back(newP);
}

void Terrain::Update(float delta)
{
	//Ensure water terrain patch inited
	assert(terrainWater->isInit == true);

	//Current dimentions of the render target. 
	int screenWidth = Application::GetInstance()->ReturnClientWidth();
	int screenHeight = Application::GetInstance()->ReturnClientHeight();

	//If the size has changed, create the new reflect/refact map. 
	if (screenWidth != terrainWater->reflectionRefactMapSizeW ||
		screenHeight != terrainWater->reflectionRefactMapSizeH)
		terrainWater->OnResize(screenWidth, screenHeight);

	//Update water
	terrainWater->Update(delta);
}

void Terrain::Render(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		 
		VirtualCamera* playerCamera,										   
		DirecLightShadowMapData* shadowMap,									    
		SSAOData* ssaoMap,													   
		ID3D11ShaderResourceView* envMap,									    
		ID3D11ShaderResourceView* llb,                                          
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,      
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					    
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						  
		CXMMATRIX &view, CXMMATRIX &proj,									   
		ID3D11DeviceContext* context)
{
	//If shadow pass, return early since we do not have our terrain shadow casting
	//(yet). Also return early is not opaque pass (IE transparant pass)
	if ( (isShadowPass) || (!isOpaquePass) )
		return;

	//Do frustum culling - if it passes, add to batch. We are only doing
	//the frustum culling once per frame. Those that pass the test here, 
	//would also pass when we do colour writes. 
	if (isZPrePass)
		PreformFrustumCull(playerCamera);

	//Render those that have passed.
	std::vector<TerrainPatchData*>::iterator it;
	it = terrainPatchesToRender.begin();
	while (it != terrainPatchesToRender.end())
	{
		(*it)->Patch->Render(isZPrePass, isOpaquePass, isShadowPass,		 
				playerCamera,										   
				shadowMap,									    
				ssaoMap,													   
				envMap,									    
				llb,                                          
				llibO, llsebO,      
				llibT, llsebT,     
				dLightArr, dLightCount,					    
				hemiColDown, hemiColRange,						  
				view, proj,									   
				context);

		//Next patch that has passed. 
		it++;
	}

	//if opaque pass, create the reflection & refaction map prior to 
	//render the water. 
	if (!isZPrePass)
	{
		terrainWater->RenderRefaction(isZPrePass, isOpaquePass, isShadowPass,		 
				playerCamera,										   
				shadowMap,									    
				ssaoMap,													   
				skyDome->ReturnCubeMapSRV(),									    
				llb,                                          
				llibO, llsebO,      
				llibT, llsebT,     
				dLightArr, dLightCount,					    
				hemiColDown, hemiColRange,						  
				view, proj,									   
				context, 
				terrainPatches);

		terrainWater->RenderReflection(isZPrePass, isOpaquePass, isShadowPass,		 
				playerCamera,										   
				shadowMap,									    
				ssaoMap,													   
				skyDome->ReturnCubeMapSRV(),									    
				llb,                                          
				llibO, llsebO,      
				llibT, llsebT,     
				dLightArr, dLightCount,					    
				hemiColDown, hemiColRange,						  
				view, proj,									   
				context, 
				terrainPatches);
	}

	//Draw water
	terrainWater->Render(isZPrePass, isOpaquePass, isShadowPass,		 
				playerCamera,										   
				shadowMap,									    
				ssaoMap,													   
				skyDome->ReturnCubeMapSRV(),									    
				llb,                                          
				llibO, llsebO,      
				llibT, llsebT,     
				dLightArr, dLightCount,					    
				hemiColDown, hemiColRange,						  
				view, proj,									   
				context);
}

void Terrain::PreformFrustumCull(VirtualCamera* cam)
{
	std::vector<TerrainPatchData*>::iterator it;
	it = terrainPatches.begin();
	while (it != terrainPatches.end())
	{
		if (PreformFrustumTestOnPatch((*it), cam))
			terrainPatchesToRender.push_back((*it));

		//Next patch
		it++;
	} 
#ifdef DEBUG_OUTPUT_TERRAIN_FRUSTUM_CULL_STATS
	std::stringstream ss, ss2, ss3, ss4;
	ss << terrainPatchesToRender.size(); //Passed
	ss2 << terrainPatches.size();        //Tot patches
	
	//Output stats.
	OutputDebugStringW(L"Terrain Frustum Culling Stats: Rendering:  ");
	OutputDebugStringA(ss.str().c_str());
	OutputDebugStringW(L" Patches Out Of: ");
	OutputDebugStringA(ss2.str().c_str());
	OutputDebugStringW(L"\n");
#endif
}

int Terrain::PreformFrustumTestOnPatch(TerrainPatchData* patch, VirtualCamera* cam)
{
	//Transform camera frustum to local space of this model. 
	XNA::Frustum frustumL;
	cam->ReturnFrustumInLocalSpace(&patch->Patch->worldMatrix, &frustumL);

	//Preform frustum - AABB intersection test in local space of the object. If it
	//passes, add to the successful batch list. 
	//
	//1 == Partial intersection, 2 == box is fully inside the frustum. 0 == outside.
	return XNA::IntersectAxisAlignedBoxFrustum(&patch->PatchAABBL, &frustumL); 
}

void Terrain::OnBatchAdd(GraphicsInterface* sharedGraphicsInterface)
{
	//Clear the vector of patches that have passed the frustum test as this
	//is a new frame.
	terrainPatchesToRender.clear();

	//Add all static models to batch - we let the standard renderer
	//handle this therefore we must add things to the batch before the scene
	//programmer calls Render(). 
	for (unsigned i = 0; i < terrainStaticModels.size(); i++)
		sharedGraphicsInterface->AddModelToBatch(terrainStaticModels[i]);
}

void Terrain::CalculateMainTerrainSize()
{
	XMFLOAT3 position;   //World space position (center point)
	XMFLOAT3 maxPosition; //World space max X and Z positions of this patch
	XMFLOAT3 minPosition; //World space min X and Z positions of this patch. 
	float cellSize;      //World space size of each poly (square units)
	float terrainVertX;  //Number of verticies in the X direction (size)
	float terrainVertZ;  //Number of verticies in the Z direction (size)
	float sizeX;         //Size in X direction of the patch in world space units (Take in to account cellSize)  
	float sizeZ;         //Size in Z direction of the patch in world space units.
	TerrainPatch* patch; //Pointer to a patch.

	for (unsigned i = 0; i < terrainPatches.size(); i++)
	{
		//Get the ith patch of the main terrain. 
		patch = terrainPatches[i]->Patch;

		//Extract needed data
		position = patch->GetWorldPosition();
		cellSize = patch->terrainCellSize;
		terrainVertX = patch->terrainWidth;
		terrainVertZ = patch->terrainDepth;

		//Calc size of the patch
		sizeX = cellSize * terrainVertX;
		sizeZ = cellSize * terrainVertZ;

		//Calculate the maximum positions. 
		maxPosition.x = position.x + sizeX / 2.0f;
		maxPosition.z = position.z + sizeZ / 2.0f;

		//And the minimum positions
		minPosition.x = position.x - sizeX / 2.0f;
		minPosition.z = position.z - sizeZ / 2.0f;

		//Update the max positions
		if (maxPosition.x > mainTerrainSizeMaxX)
			mainTerrainSizeMaxX = maxPosition.x;
		if (maxPosition.z > mainTerrainSizeMaxZ)
			mainTerrainSizeMaxZ = maxPosition.z;

		//And minimum positions
		if (minPosition.x < mainTerrainSizeMinX)
			mainTerrainSizeMinX = minPosition.x;
		if (minPosition.z < mainTerrainSizeMinZ)
			mainTerrainSizeMinZ = minPosition.z;
	}

	//Output dimentions
#ifdef DEBUG_OUTPUT_TERRAIN_MIN_MAX_DIMENTIONS
	std::stringstream ss1, ss2, ss3, ss4;
	ss1 << mainTerrainSizeMinX;
	ss2 << mainTerrainSizeMaxX;
	ss3 << mainTerrainSizeMinZ;
	ss4 << mainTerrainSizeMaxZ;

	OutputDebugStringW(L"Terrain Size : ");
	OutputDebugStringA(ss1.str().c_str());
	OutputDebugStringW(L"|");
	OutputDebugStringA(ss2.str().c_str());
	OutputDebugStringW(L"|");
	OutputDebugStringA(ss3.str().c_str());
	OutputDebugStringW(L"|");
	OutputDebugStringA(ss4.str().c_str());
	OutputDebugStringW(L"|\n");
#endif
}

//float Terrain::GetHeight(float x, float z)
//{
//	std::stringstream ss, ss2;
//
//	ss << x;
//	ss2 << z;
//
//	//OutputDebugStringW(L"X = ");
//	//OutputDebugStringA(ss.str().c_str());
//	//OutputDebugStringW(L" Z = ");
//	//OutputDebugStringA(ss2.str().c_str());
//
//
//	//Incorperate the cell size. 
//	x /= TERRAIN_CELL_SIZE;
//	z /= TERRAIN_CELL_SIZE;
//
//	std::stringstream ss3, ss4;
//
//	ss3 << x;
//	ss4 << z;
//
//	OutputDebugStringW(L" X Div = ");
//	OutputDebugStringA(ss3.str().c_str());
//	OutputDebugStringW(L" Z Div = ");
//	OutputDebugStringA(ss4.str().c_str());
//
//	//Ensure in main terrain bounds. 
//	if (x > (mainTerrainSizeMaxX/TERRAIN_CELL_SIZE))
//	{
//		OutputDebugStringW(L"Outside Terrain: Max X\n");
//		return -1.0f;
//	}
//	else if (x < (mainTerrainSizeMinX/TERRAIN_CELL_SIZE))
//	{
//		OutputDebugStringW(L"Outside Terrain: Min X\n");
//		return -1.0f;
//	}
//	else if (z > (mainTerrainSizeMaxZ/TERRAIN_CELL_SIZE))
//	{
//		OutputDebugStringW(L"Outside Terrain: Max Z\n");
//		return -1.0f;
//	}
//	else if (z < (mainTerrainSizeMinZ/TERRAIN_CELL_SIZE))
//	{
//		OutputDebugStringW(L"Outside Terrain: Min Z\n");
//		return -1.0f;
//	}
//
//	//We are in bounds.
//	//
//	//How far in the x or z direction are we compared to the maximum 
//	//x and z direction.
//	float xLocPct = (x - mainTerrainSizeMinX) / TERRAIN_SIZE; 
//	float zLocPct = (z - mainTerrainSizeMinZ) / TERRAIN_SIZE;
//
//	//Where to sample the 2D heights array in the range: [0, TERRAIN_SIZE-1]
//	float xSamp = xLocPct * (TERRAIN_SIZE);
//	float zSamp = zLocPct * (TERRAIN_SIZE);
//
//	//Flip z sample location from [0,TERRAIN_SIZE-1] to [TERRAIN_SIZE-1, 0]
//	//because the heights in the array in the z direction are upside down
//	//from the way we look at things. 
//	//
//	//abs() -> absolute value (alwyas +. Thus abs(-100) == +100)
//	zSamp = abs((zSamp - (TERRAIN_SIZE)));
//
//	//Where to sample in our 2D array - UINT.
//	int zSample = (int)zSamp; //Avoids going over the end of the array.
//	int xSample = (int)xSamp;
//
//	std::stringstream ss5, ss6;
//	ss5 << xSample; 
//	ss6 << zSample;
//
//	OutputDebugStringW(L" X Samp = ");
//	OutputDebugStringA(ss5.str().c_str());
//	OutputDebugStringW(L" Z Samp = ");
//	OutputDebugStringA(ss6.str().c_str());
//
//	OutputDebugStringW(L"\n");
//
//	//Sample the heights that make up the quad that he player is in. We then interpolate
//	//to get a smooth height for any value inside the quad. (p is player location within
//	//)
//	//
//	// 0 *------*   1
//	//   |  p   |
//	//   |	  |
//	// 2 *------*   3
//
//	//Sample 0 - if we are near the edge, just return this. 
//	float height0 = terrainHeights[zSample][xSample];
//	//Sample these later - once we know the sample wont go out of the bounds
//	//of the array crashing the app. 
//	float height1;
//	float height2;
//	float height3;
//
//	//Return value - init to height 0 incase we are right at the edge
//	//and dont interpolate. 
//	float retHeight = height0;
//
//	//Ensure sampling will be in range...
//	if ((xSample+1 < TERRAIN_SIZE) && (zSample+1 < TERRAIN_SIZE))
//	{
//		//Calc interplation factors (How far are we in to the quad.)
//		int intpart = (int)x;
//		float interpFactorX = 1.0f-(abs(x-intpart));
//		intpart = (int)z;
//		float interpFactorZ = (abs(z-intpart)); //Z Axis is flipped. 
//
//		//In bounds - sample. We then interpolate to generate the height at a given 
//		//world space location
//		height1 = terrainHeights[zSample][xSample+1];
//		height2 = terrainHeights[zSample+1][xSample];
//		height3 = terrainHeights[zSample+1][xSample+1];
//
//		//lerp in X direction
//		float h01 = height1 + ((height0-height1)*interpFactorX);
//		float h23 = height3 + ((height2-height3)*interpFactorX);
//		
//		//Calculate the return (lerp in Z direction between h01 and h23.
//		retHeight = h23 + ((h01 - h23)*interpFactorZ);
//	}
//
//	//Add global translation
//	retHeight+=TERRAIN_TRANSLATION_Y;
//
//	//Return.
//	return retHeight;
//}

float Terrain::GetDepth()
{
	return (TERRAIN_SIZE-1)*TERRAIN_CELL_SIZE;
}

float Terrain::GetWidth()
{
	return (TERRAIN_SIZE-1)*TERRAIN_CELL_SIZE;
}

float Terrain::GetHeight(float x, float z)
{
	//Transform local space of terrain from (x,z) 0,0 being bottom left
	//to right in the middle of the terrain. 
	x -= GetWidth()/2.0f;
	z -= GetDepth()/2.0f;

	//Transform from the terrain local space to cell space. 
	float co = (x + 0.5f*GetWidth()) / TERRAIN_CELL_SIZE;
	float dp = (z - 0.5f*GetDepth()) / -TERRAIN_CELL_SIZE;

	//Get the row and column we are in
	int row = (int)floorf(dp);
	int col = (int)floorf(co);

	//std::stringstream ss, ss2;
	//ss << row;
	//ss2 << col;

	//OutputDebugStringW(L"Row ID = ");
	//OutputDebugStringA(ss.str().c_str());
	//OutputDebugStringW(L" Collumn ID = ");
	//OutputDebugStringA(ss2.str().c_str());
	//OutputDebugStringW(L"\n");

	//Ensure we do not sample out of bounds
	if ( (row+1 >= TERRAIN_SIZE) || (col+1 >= TERRAIN_SIZE) )
		return -1.0f;

	//Get the heights of the cell we are in. 
	// A*--*B
	//  | /|
	//  |/ |
	// C*--*D
	float a = terrainHeights[row][col];
	float b = terrainHeights[row][col+1];
	float c = terrainHeights[row+1][col];
	float d = terrainHeights[row+1][col+1];

	//Find out where we are relative to the cell. 
	float s = co - (float)col;
	float t = dp - (float)row;

	//Init return height
	float retHeight = 0.0f;

	//If we are in the upper triangle, ABC
	if (s + t <= 1.0f)
	{
		float uy = b - a;
		float vy = c - a;

		retHeight =  a + s*uy + t*vy;
	}
	else //Else, in bottom triangle DCB
	{
		float uy = c - d;
		float vy = b - d;
		retHeight =  d + (1.0f - s) * uy + (1.0f-t)*vy;
	}

	//Add global translation
	retHeight+=TERRAIN_TRANSLATION_Y;

	//Return.
	return retHeight;
}

void Terrain::DebugDrawRefactionTexture(ID3D11DeviceContext* context)
{
	terrainWater->DebugDrawRefactionTexture(context);
}

void Terrain::DebugDrawReflectionTexture(ID3D11DeviceContext* context)
{
	terrainWater->DebugDrawReflectionTexture(context);
}